#!/usr/bin/env python

# (c) 2007-2011 Helsinki University of Technology and University of Helsinki
# Licensed under the MIT license; see LICENSE.txt for more information.

from worker import Worker
from lrucache import LRUCache
from rdf import *

import sys
import urllib2
import re
import dateutil.parser

MAX_ATTEMPT = 5
CACHE_SIZE = 30

W3CDTF_FORMATS = (
  re.compile(r'^\d{4}-\d\d-\d\dT\d\d:\d\d:\d\d\.\d+(Z|([+-]\d\d:\d\d))$'),
  re.compile(r'^\d{4}-\d\d-\d\dT\d\d:\d\d:\d\d(Z|([+-]\d\d:\d\d))$'),
  re.compile(r'^\d{4}-\d\d-\d\dT\d\d:\d\d(Z|([+-]\d\d:\d\d))$'),
  re.compile(r'^\d{4}-\d\d-\d\d$'),
  re.compile(r'^\d{4}-\d\d$'),
  re.compile(r'^\d{4}$'),
)

class Validator (Worker):
  def __init__(self, session):
    Worker.__init__(self, session.getFatalEvent())
    self._session = session
    self._cache = LRUCache(CACHE_SIZE)
    self._validators = {}

  def validate_w3cdtf(self, value):
    success = False
    if not value:
      return False
    for fmtre in W3CDTF_FORMATS:
      try:
        if fmtre.match(value):
          success = True
          break
      except TypeError:
        print >>sys.stderr, "Invalid type for W3CDTF validation:", type(value)
        return False
    
    try:
      dateutil.parser.parse(value)
    except ValueError:
      success = False
      
    return success

  def validate_concept(self, uri, rdftype=None, inscheme=None, attempt=1):
    if not uri:
      return False
  
    cachekey = (uri, rdftype, inscheme)
    if cachekey in self._cache:
      return self._cache[cachekey]
  
    req = urllib2.Request(uri, None, {'Accept': 'application/rdf+xml'})
    try:
      fh = urllib2.urlopen(req)
      graph = getGraph()
      graph.parse(fh)
    except:
      print >>sys.stderr, "Validation error for concept", uri, "attempt:", attempt
      if attempt >= MAX_ATTEMPT:
        print >>sys.stderr, "Giving up"
        return True # assume it's valid
      return self.validate_concept(uri, rdftype, inscheme, attempt+1)
    fh.close()

    success = True
    if rdftype and (uri, RDF.type, rdftype) not in graph:
      success = False
    if inscheme and (uri, SKOS.inScheme, inscheme) not in graph:
      success = False
    self._cache[cachekey] = success
    return success

  def check_required(self, doc, fld, fldname, single=False, warnonly=False):
    vals = doc.getMetadata(fld)
    if not vals:
      if warnonly:
        doc.log('WARN', 'validate', 'Missing required field', fldname)
      else:
        doc.log('CRIT', 'validate', 'Missing required field', fldname)
        return False

    if single:
      self.check_single(doc, fld, fldname)
    
    return True
  
  def check_single(self, doc, fld, fldname):
    vals = doc.getMetadata(fld)
    
    langvals = {}
    for val in vals:
      if isinstance(val, Literal):
        lang = val.language
      else:
        lang = 'resource'
      if lang in langvals:
        doc.log('WARN', 'validate', 'Multiple values given in field, picking one', "%s (%s)" % (fldname, lang))
        doc.clearMetadata(fld)
        doc.setMetadata(fld, langvals[lang])
      langvals[lang] = val
        


  def process(self, doc):
    ctype = list(doc.getMetadata(RDF.type))[0]
    if ctype not in self._validators:
      validator = get_validator(ctype)
      if validator is None:
        print >>sys.stderr, "No validators found for type", ctype
        return
      self._validators[ctype] = validator(self._session)
    return self._validators[ctype].process(doc)
  
  def finish(self):
    for validator in self._validators.values():
      validator.finish()
    del self._cache

# validator plugin registry
_validators = {}

def register_validator(ctype, cl):
  _validators[ctype] = cl

def get_validator(ctype):
  cl = _validators.get(ctype, None)
#  if not cl:
#    print >>sys.stderr, "No validators found for type", ctype
  return cl

# import the validators so they can register themselves
from validator import *
